/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ufpb.compiladores.lexico.impl;

import java.util.Stack;
import org.w3c.dom.*;
import ufpb.compiladores.lexico.Automaton;
import ufpb.compiladores.lexico.Enviroment;
import ufpb.compiladores.lexico.LexicalTable;
import ufpb.compiladores.lexico.Reader;

/**
 *
 * @author Sarah
 */
public class AutomatonImpl extends Automaton{

    public AutomatonImpl (Enviroment env, Reader reader){
            super(env, reader);
    }
    
    @Override
    public LexicalTable generateTable(Document doc, Element e) {

        int contaLinha = 1;
        String token = null;
        String descricao;
        String EA = env.getInitialState();
        String aux;
        StringBuilder bufferDeCaracteres = new StringBuilder();
        Stack<String> pilhaDeEstados = new Stack<String>();
        char c;
        boolean inComent = false;


        /* Cria o arquivo XML */

//        DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
//        DocumentBuilder docBuilder = null;
//        try {
//            docBuilder = dbfac.newDocumentBuilder();
//        } catch (ParserConfigurationException ex) {
//            Logger.getLogger(AutomatonImpl.class.getName()).log(Level.SEVERE, null, ex);
//        }
//
//        Document doc = docBuilder.newDocument();
        
        Element tabela = doc.createElement("Tabela");

        Element erros = doc.createElement("Erros");

        LexicalTable lt = new LexicalTable();


        /* Inicia o autômato */
        int linha = 0;
        boolean finish = false;
        while (!finish){

            if (!reader.nextCharacter()) {
                
                finish = true;
            }
            c = reader.getCurrentCharacter();         
            
            //Trata comentário
            if (env.getComentOpen().matches("[" + c + "]")) {
                
                inComent = true;
                linha = contaLinha;
                continue;
            } else if (env.getComentClose().matches("[" + c + "]")) {

                if (!inComent){

                    Element erro = doc.createElement("Erro");
                    erro.setAttribute("erro", "Comentário fechado sem abrir");
                    erro.setAttribute("linha", String.valueOf(contaLinha));
                    erros.appendChild(erro); //define o node erro como subnode de erros
                    //reinicia o autômato
                    bufferDeCaracteres.delete(0, bufferDeCaracteres.length());
                    pilhaDeEstados.removeAllElements();
                    EA = env.getInitialState();
                }

                inComent = false;
                continue;
            } else if (inComent) {
                
                if (c == '\n') {
                    
                    contaLinha++;
                }
                
                continue;
            }
            
           // if ((c != '\t') && (c != '\r')) {
                
                aux = env.transition(EA, c); //verifica se tem transição possível
                
                if (aux != null) { //se tiver
                    
                    pilhaDeEstados.push(EA); //empilha como estado atual
                    EA = aux;
                    if (c != ' ') {
                        
                        bufferDeCaracteres.append(c); //adiciona o caracter ao buffer
                    }
                
                } else {
                 
                    if (env.isFinalState(EA)) { //preencher a tabela
                
                        token = bufferDeCaracteres.toString();
                        descricao = env.tokenDescription(token);
                        if (descricao == null) {
                            
                            descricao = env.stateDescription(EA);
                        }
                        if (descricao == null) {
                            
                            descricao = "Identificador";
                        }
                        
                        Element elemento = doc.createElement("Elemento"); //cria o node elemento
                        elemento.setAttribute("token", token);
                        elemento.setAttribute("descricao", descricao);
                        elemento.setAttribute("linha", String.valueOf(contaLinha));
                        tabela.appendChild(elemento); //define o node elemento como subnode de tabela

                        lt.addEntry(token, descricao, contaLinha);

                        //reinicia o autômato
                        bufferDeCaracteres.delete(0, bufferDeCaracteres.length());
                        pilhaDeEstados.removeAllElements();
                        EA = env.getInitialState();
                        reader.previousCharacter();
                    } else {
                        
                        while (!env.isFinalState(EA) && !env.isInitialState(EA)) {

                            EA = String.valueOf(pilhaDeEstados.pop());
                            bufferDeCaracteres.deleteCharAt(bufferDeCaracteres.length()-1);
                            reader.previousCharacter();                   
                        }

                        if (env.isInitialState(EA)) {
                            
                            if (c == '\n') {
                                
                                contaLinha++;
                            } else if (c != '\r' && c != '\t') {
                                
                                Element erro = doc.createElement("Erro");
                                erro.setAttribute("erro", "Caracter " + c + " não pertence à linguagem");
                                erro.setAttribute("linha", String.valueOf(contaLinha));
                                erros.appendChild(erro); //define o node erro como subnode de erros
                                //reinicia o autômato
                                bufferDeCaracteres.delete(0, bufferDeCaracteres.length());
                                pilhaDeEstados.removeAllElements();
                                EA = env.getInitialState();
                            }
                        } else {
                            
                            token = bufferDeCaracteres.toString();   
                            descricao = env.tokenDescription(token);
                            if (descricao == null) {
                            
                                descricao = env.stateDescription(EA);
                            }
                            if (descricao == null) {

                                descricao = "Identificador";
                            }
                            Element elemento = doc.createElement("elemento"); //cria o node elemento
                            elemento.setAttribute("token", token);
                            elemento.setAttribute("descricao", descricao);
                            elemento.setAttribute("linha", String.valueOf(contaLinha));
                            tabela.appendChild(elemento); //define o node elemento como subnode de tabela 

                            lt.addEntry(token, descricao, contaLinha);

                            //reinicia o autômato
                            bufferDeCaracteres.delete(0, bufferDeCaracteres.length()-1);
                            pilhaDeEstados.removeAllElements();
                            EA = env.getInitialState();
                            reader.previousCharacter();
                        }
                    }
                }         
        }  

        //Se chegamos ao final e estamos ainda em comentário, temos um erro.
        if (inComent) {
            
            Element comErr = doc.createElement("Erro");
            comErr.setAttribute("erro", "Comentário aberto sem fechar");
            comErr.setAttribute("linha", Integer.toString(linha));
            erros.appendChild(comErr);
        }
        
        Element root = doc.createElement("Lexical");
        root.appendChild(tabela);
        root.appendChild(erros);
        e.appendChild(root);

        //lt.showArrayList();

        return lt;
    }

}


